#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "crypto/types.h"
#include "crypto/tool.h"
#include "crypto/json.h"
#include "crypto/parse/json/ParseJsonImp.h"

static char *JsonStringify(Json *json, char *buffer);
static bool JsonKeyJudge(char *key);
static uint JsonKeyHash(char *key);
static void InitJsonEntry(JsonEntry *jsonEntry);
static void JsonValueArrayFree(JsonArray *jsonArray);
static void JsonValueFree(JsonValue *jsonValue);
static void JsonIndexItemFree(JsonIndexItem *jsonIndexItem);
static void JsonEntryFree(JsonEntry *jsonEntry);
static void JsonEntryTransform(JsonEntry *jsonEntry);
static JsonIndexItem *JsonIndexItemInit();
static void JsonAddJsonIndexItemToEntry(JsonEntry *jsonEntry, JsonIndexItem *jsonIndexItem);
static void JsonAddJsonItemToEntry(JsonEntry *jsonEntry, JsonItem *jsonItem);
static void JsonResize(Json *json);

static char jsonStringifyBuffer[10240];
/*
 * Like Java HashMap. Initial CAPACITY is 16.
 * And PAYLOAD is 0.75.
 * When the number of json items are over than
 * the CAPACITY * PAYLOAD,
 * the json will resize the CAPACITY to 2 * CAPACITY.
 * When the size of a hash slot is over than 8,
 * the data struct of hash slot will become
 * BINARY TREE from LINK LIST.
 */
static bool JsonKeyJudge(char *key)
{
    return _strnlen(key, JSON_KEY_LENGTH) == JSON_KEY_LENGTH ? false : true;
}

static uint JsonKeyHash(char *key)
{
    const uint base = 129;
    uint len = strlen(key), num = 0;
    for (int i = 0; i < len; i++)
        num = num * base + (uint)key[i]; // 权值展开
    return num;
}

static void InitJsonEntry(JsonEntry *jsonEntry)
{
    jsonEntry->len = 0;
    jsonEntry->indexItem = NULL;
    jsonEntry->type = JSON_DATA_STRUCT_LINKLIST;
}

// free Json

static void JsonValueArrayFree(JsonArray *jsonArray)
{
    switch (jsonArray->type)
    {
    case JSON_VALUE_TYPE_LONG:
    case JSON_VALUE_TYPE_DOUBLE:
        free(jsonArray->array);
        break;
    case JSON_VALUE_TYPE_STRING:
        for (int i = 0; i < jsonArray->len; i++)
        {
            free(((char **)jsonArray->array)[i]);
        }
        break;
    case JSON_VALUE_TYPE_ARRAY:
        for (int i = 0; i < jsonArray->len; i++)
        {
            JsonValueArrayFree(((JsonArray **)jsonArray->array)[i]);
        }
        break;
    case JSON_VALUE_TYPE_JSON:
        for (int i = 0; i < jsonArray->len; i++)
        {
            JsonFree(((Json **)jsonArray->array)[i]);
        }
        break;
    case JSON_VALUE_TYPE_NUL:
        break;
    case JSON_VALUE_TYPE_INVALI:
        assert(false);
    }
    free(jsonArray);
}

static void JsonValueFree(JsonValue *jsonValue)
{
    switch (jsonValue->type)
    {
    case JSON_VALUE_TYPE_LONG:
    case JSON_VALUE_TYPE_DOUBLE:
    case JSON_VALUE_TYPE_STRING:
        free(jsonValue->value);
        break;
    case JSON_VALUE_TYPE_ARRAY:
        JsonValueArrayFree(jsonValue->value);
        break;
    case JSON_VALUE_TYPE_JSON:
        JsonFree(jsonValue->value);
        break;
    case JSON_VALUE_TYPE_NUL:
        break;
    case JSON_VALUE_TYPE_INVALI:
        assert(false);
    }
}

void JsonItemFree(JsonItem *jsonItem)
{
    assert(jsonItem != NULL);
    JsonValueFree(jsonItem->value);
    free(jsonItem);
}

static void JsonIndexItemFree(JsonIndexItem *jsonIndexItem)
{
    JsonItemFree(jsonIndexItem->item);
    free(jsonIndexItem);
}

static void JsonEntryFree(JsonEntry *jsonEntry)
{
    if (jsonEntry->type == JSON_DATA_STRUCT_LINKLIST)
    {
        JsonIndexItem *now, *next;
        now = jsonEntry->indexItem;
        while (now != NULL)
        {
            next = now->index.linkList.next;
            JsonIndexItemFree(now);
            now = next;
        }
    }
    else
    {
        assert(false);
    }
}

void JsonFree(Json *json)
{
    int i = json->cap;
    while (i != 0)
    {
        i--;
        JsonEntryFree(&(json->entrys[i]));
    }
    free(json->entrys);
    free(json);
}
/*
 * later deal
 */
static void JsonEntryTransform(JsonEntry *jsonEntry)
{
    // transform to balence binary tree
}

static JsonIndexItem *JsonIndexItemInit()
{
    JsonIndexItem *jsonIndexItem =
        (JsonIndexItem *)malloc(sizeof(JsonIndexItem));
    jsonIndexItem->index.binaryTree.left = NULL;
    jsonIndexItem->index.binaryTree.right = NULL;
    return jsonIndexItem;
}

static void JsonAddJsonIndexItemToEntry(JsonEntry *jsonEntry, JsonIndexItem *jsonIndexItem)
{
    jsonEntry->len++;
    jsonIndexItem->index.binaryTree.left = NULL;
    jsonIndexItem->index.binaryTree.right = NULL;
    if (jsonEntry->len > 8)
    {
        JsonEntryTransform(jsonEntry);
    }
    if (jsonEntry->type == JSON_DATA_STRUCT_LINKLIST)
    {
        if (jsonEntry->indexItem == NULL)
        {
            jsonEntry->indexItem = jsonIndexItem;
        }
        JsonIndexItem *tmp = jsonEntry->indexItem;
        while (tmp->index.linkList.next != NULL)
        {
            tmp = tmp->index.linkList.next;
        }
        tmp->index.linkList.next = jsonIndexItem;
    }
    else
    {
        assert(false);
    }
}

static void JsonAddJsonItemToEntry(JsonEntry *jsonEntry, JsonItem *jsonItem)
{
    jsonEntry->len++;
    if (jsonEntry->len == 8)
    {
        JsonEntryTransform(jsonEntry);
    }
    if (jsonEntry->type == JSON_DATA_STRUCT_LINKLIST)
    {
        if (jsonEntry->indexItem == NULL)
        {
            JsonIndexItem *jsonIndexItem = JsonIndexItemInit();
            jsonIndexItem->item = jsonItem;
            jsonEntry->indexItem = jsonIndexItem;
            return;
        }
        JsonIndexItem *tmp = jsonEntry->indexItem;
        while (tmp->index.linkList.next != NULL)
        {
            // 有重去重
            if (!strcmp(tmp->item->key, jsonItem->key))
            {
                JsonItemFree(tmp->item);
                tmp->item = jsonItem;
                return;
            }
            tmp = tmp->index.linkList.next;
        }
        // 有重去重
        if (!strcmp(tmp->item->key, jsonItem->key))
        {
            JsonItemFree(tmp->item);
            tmp->item = jsonItem;
            return;
        }
        JsonIndexItem *jsonIndexItem = JsonIndexItemInit();
        jsonIndexItem->item = jsonItem;
        tmp->index.linkList.next = jsonIndexItem;
    }
    else
    {
        assert(false);
    }
}

static void JsonResize(Json *json)
{
    int oldCap = json->cap;
    json->cap *= 2;
    JsonEntry *newJsonEntrys =
        (JsonEntry *)malloc(sizeof(JsonEntry) * json->cap);
    for (int i = 0; i < json->cap; i++)
    {
        InitJsonEntry(&(json->entrys[i]));
    }

    int newHashBase = json->cap - 1;
    JsonIndexItem *jsonIndexItem;
    JsonIndexMeth *jsonIndexMeth;

    // transport jsonIndexItem from old newJsonEntry to new JsonEntry;
    for (int i = 0; i < oldCap; i++)
    {
        jsonIndexItem = json->entrys[i].indexItem;
        if (json->entrys[i].type == JSON_DATA_STRUCT_LINKLIST)
        {
            while (jsonIndexItem != NULL)
            {
                jsonIndexMeth = &(jsonIndexItem->index);
                JsonAddJsonIndexItemToEntry(&(newJsonEntrys[jsonIndexItem->item->hash & newHashBase]), jsonIndexItem);
                jsonIndexItem = jsonIndexMeth->linkList.next;
            }
        }
        else
        {
            while (true)
            {
                assert(false);
            }
        }
    }
    free(json->entrys);
    json->entrys = newJsonEntrys;
}

Json *JsonInit()
{
    Json *json = (Json *)malloc(sizeof(Json));
    json->cap = JSON_DEFAULT_INITIAL_CAPACITY;
    json->len = 0;
    json->entrys = (JsonEntry *)malloc(JSON_DEFAULT_INITIAL_CAPACITY * sizeof(JsonEntry));
    for (int i = 0; i < JSON_DEFAULT_INITIAL_CAPACITY; i++)
    {
        InitJsonEntry(json->entrys + i);
    }
    return json;
};

JsonValue *JsonValueInit(JsonValueType type)
{
    JsonValue *jsonValue = (JsonValue *)malloc(sizeof(JsonValue));
    jsonValue->type = type;
    jsonValue->value = NULL;
    return jsonValue;
}

JsonItem *JsonItemInit(char *key, JsonValue *value)
{
    JsonItem *jsonItem = (JsonItem *)malloc(sizeof(JsonItem));
    jsonItem->hash = JsonKeyHash(key);
    assert(JsonKeyJudge(key));
    strcpy(jsonItem->key, key);
    jsonItem->value = value;
    return jsonItem;
}

JsonArray *JsonArrayInit()
{
    JsonArray *jsonArray = (JsonArray *)malloc(sizeof(JsonArray));
    jsonArray->cap = 0;
    jsonArray->len = 0;
    return jsonArray;
}

bool JsonPut(Json *json, JsonItem *jsonItem)
{
    assert(JsonKeyJudge(jsonItem->key));
    if (json->cap * JSON_PAYLOAD < json->len)
    {
        JsonResize(json);
    }
    int index = jsonItem->hash % json->cap;
    JsonAddJsonItemToEntry(&(json->entrys[index]), jsonItem);
    json->len++;
    return true;
}

bool JsonRemove(Json *json, char *key)
{
    assert(JsonKeyJudge(key));
    uint hash = JsonKeyHash(key);
    int index = hash % json->cap;
    JsonEntry *jsonEntry = &(json->entrys[index]);
    if (jsonEntry->type == JSON_DATA_STRUCT_LINKLIST)
    {
        JsonIndexItem *pre, *now;
        pre = NULL;
        now = jsonEntry->indexItem;
        while (now != NULL && _strncmp(key, now->item->key, JSON_KEY_LENGTH))
        {
            pre = now;
            now = now->index.linkList.next;
        }
        if (now == NULL)
        {
            return false;
        }
        else
        {
            if (pre == NULL)
            {
                jsonEntry->indexItem = now->index.linkList.next;
            }
            else
            {
                pre->index.linkList.next = now->index.linkList.next;
            }
            JsonIndexItemFree(now);
        }
    }
    else
    {
        assert(false);
    }
    return true;
}

static void JsonArrayStringify(JsonArray *jsonArray, char *buffer)
{
    int tmp = strlen(buffer);
    buffer[tmp] = '[';
    buffer[tmp + 1] = '\0';
    int len = jsonArray->len;

    switch (jsonArray->type)
    {
    case JSON_VALUE_TYPE_LONG:
        for (int i = 0; i < len; i++)
        {
            sprintf(buffer, "%s%lld,", buffer, (((int64 *)jsonArray->array)[i]));
        }
    case JSON_VALUE_TYPE_DOUBLE:
        for (int i = 0; i < len; i++)
        {
            sprintf(buffer, "%s%.3lf,", buffer, (((double *)jsonArray->array)[i]));
        }
        break;
    case JSON_VALUE_TYPE_STRING:
        for (int i = 0; i < len; i++)
        {
            sprintf(buffer, "%s\"%s\",", buffer, ((char **)jsonArray->array)[i]);
        }
        break;
    case JSON_VALUE_TYPE_ARRAY:
        for (int i = 0; i < len; i++)
        {
            JsonArrayStringify(((JsonArray **)jsonArray->array)[i], buffer);
            tmp = strlen(buffer);
            buffer[tmp] = ',';
            buffer[tmp + 1] = '\0';
        }
        break;
    case JSON_VALUE_TYPE_JSON:
        for (int i = 0; i < jsonArray->len; i++)
        {
            JsonStringify(((Json **)jsonArray->array)[i], buffer);
            tmp = strlen(buffer);
            buffer[tmp] = ',';
            buffer[tmp + 1] = '\0';
        }
        break;
    case JSON_VALUE_TYPE_NUL:
        break;
    case JSON_VALUE_TYPE_INVALI:
        assert(false);
    }
    buffer[strlen(buffer) - 1] = ']';
}

static void JsonEntryStringify(JsonEntry *jsonEntry, char *buffer)
{
    if (jsonEntry->len == 0)
    {
        return;
    }
    JsonIndexItem *jsonIndexItem = jsonEntry->indexItem;
    JsonValue *jsonValue;
    if (jsonEntry->type == JSON_DATA_STRUCT_LINKLIST)
    {
        while (jsonIndexItem != NULL)
        {
            jsonValue = jsonIndexItem->item->value;
            switch (jsonValue->type)
            {
            case JSON_VALUE_TYPE_LONG:
                sprintf(buffer, "%s%s:%lld,",
                        buffer, jsonIndexItem->item->key,
                        *((int64 *)jsonValue->value));
                break;
            case JSON_VALUE_TYPE_DOUBLE:
                sprintf(buffer, "%s%s:%.3lf,",
                        buffer, jsonIndexItem->item->key,
                        *((double *)jsonValue->value));
                break;
            case JSON_VALUE_TYPE_NUL:
                sprintf(buffer, "%s%s:%s,",
                        buffer, jsonIndexItem->item->key,
                        "null");
                break;
            case JSON_VALUE_TYPE_STRING:
                sprintf(buffer, "%s%s:\"%s\",",
                        buffer, jsonIndexItem->item->key,
                        (char *)jsonValue->value);
                break;
            case JSON_VALUE_TYPE_ARRAY:
                sprintf(buffer, "%s%s:",
                        buffer, jsonIndexItem->item->key);
                JsonArrayStringify((JsonArray *)(jsonValue->value), buffer);
                sprintf(buffer, "%s,", buffer);
                break;
            case JSON_VALUE_TYPE_JSON:
                sprintf(buffer, "%s%s:",
                        buffer, jsonIndexItem->item->key);
                JsonStringify((Json *)(jsonValue->value), buffer);
                sprintf(buffer, "%s,", buffer);
                break;
            case JSON_VALUE_TYPE_INVALI:
                assert(false);
            }
            jsonIndexItem = jsonIndexItem->index.linkList.next;
        }
    }
    else
    {
        assert(false);
    }
}

static char *JsonStringify(Json *json, char *buffer)
{
    char *objBuffer = buffer == NULL ? jsonStringifyBuffer : buffer;
    objBuffer[0] = '\0';
    sprintf(objBuffer, "%s%s", objBuffer, "{");
    if (json->len == 0)
    {
        sprintf(objBuffer, "%s%s",
                objBuffer, "}");
        return objBuffer;
    }
    for (int i = 0; i < json->cap; i++)
    {
        JsonEntryStringify(&(json->entrys[i]), objBuffer);
    }
    objBuffer[strlen(objBuffer) - 1] = '}';
    return objBuffer;
}

// json to string and the end char is \n
char *JsonString(Json *json, char *buffer)
{
    int len;
    char *strPtr = JsonStringify(json, buffer);
    len = strlen(strPtr);

    strPtr[len] = '\n';
    strPtr[len + 1] = 0;
    return strPtr;
}

Json *JsonParse(char *jsonString)
{
    assert(jsonString[strlen(jsonString) - 1] == '\n');
    return ParseJson(jsonString);
}

JsonItem *JsonGet(Json *json, char *key)
{
    uint hashCode = JsonKeyHash(key);
    uint mod = hashCode % json->cap;
    JsonEntry *jsonEntry = &(json->entrys[mod]);
    JsonIndexItem *tmpIndexItem = jsonEntry->indexItem;
    if (jsonEntry->type == JSON_DATA_STRUCT_LINKLIST)
    {
        while (tmpIndexItem != NULL)
        {
            if (!_strncmp(key, tmpIndexItem->item->key, JSON_KEY_LENGTH))
            {
                return tmpIndexItem->item;
            }
            tmpIndexItem = tmpIndexItem->index.linkList.next;
        }
        return NULL;
    }
    else
    {
        printf("pass");
    }
    return NULL;
}

JsonItem *JsonStringItemInit(char *key, char *data)
{
    int len = strlen(data) + 1;
    JsonValue *jsonValue = JsonValueInit(JSON_VALUE_TYPE_STRING);
    jsonValue->value = malloc(len);
    memcpy(jsonValue->value, data, len);
    return JsonItemInit(key, jsonValue);
}

JsonItem *JsonNumberInit(char *key, int64 data)
{
    JsonValue *jsonValue = JsonValueInit(JSON_VALUE_TYPE_LONG);
    jsonValue->value = malloc(sizeof(int64));
    *(int64 *)(jsonValue->value) = data;
    return JsonItemInit(key, jsonValue);
}

JsonItem *JsonDoubleInit(char *key, double data)
{
    JsonValue *jsonValue = JsonValueInit(JSON_VALUE_TYPE_DOUBLE);
    jsonValue->value = malloc(sizeof(double));
    *(double *)(jsonValue->value) = data;
    return JsonItemInit(key, jsonValue);
}